﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using EnvDTE;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.VCCodeModel;
using Microsoft.VisualStudio.TextManager.Interop;
using System.Runtime.InteropServices;
using System.Collections;

namespace GordonKristan.codeclean
{
	class RemoveModule : TextEditModule
	{
		public const uint MULTIPLE_LINE_WHITESPACE = 0x0001;
		public const uint BEGIN_FILE_WHITESPACE = 0x0002;
		public const uint END_FILE_WHITESPACE = 0x0004;
		public const uint END_LINE_WHITESPACE = 0x0008;
		public const uint END_FILE_WHITESPACE_NEWLINE = 0x0010;
		public const uint BLANK_LINE_AFTER_BRACE = 0x0020;
		public const uint BLANK_LINE_BEFORE_BRACE = 0x0040;

		/// <summary>
		/// RemoveModule constructor.
		/// </summary>
		/// <param name="aDoc">String for module to style.</param>
		public RemoveModule(String aDoc) : base(aDoc)
		{

		}

		/// <summary>
		/// Styles a documentString according the current settings.
		/// </summary>
		/// <param name="aDoc">The documentString to style.</param>
		/// <returns>The styled documentString.</returns>
		public void styleFile()
		{
			uint flags = getStyleFlags();
			removeWhitespace(flags);
		}

		/// <summary>
		/// Performs all operations that involve removing
		/// whitespace on the file. This includes removing
		/// blank lines and end of line whitespace.
		/// </summary>
		private void removeWhitespace(uint flags)
		{
			//Replace 3+ blank lines with just 2
			if ((flags & MULTIPLE_LINE_WHITESPACE) > 0)
			{
				runRegex(@"(?:\r\n){3,}", "\r\n\r\n");
			}
			//Remove beginning of file whitespace
			if ((flags & BEGIN_FILE_WHITESPACE) > 0)
			{
				runRegex(@"^(\s*)(\S)", "$2");
			}
			//Replace all end of file whitespace with just a newline
			if ((flags & END_FILE_WHITESPACE) > 0)
			{
				runRegex(@"(\S)(\s*)$", "$1");
				//Add the ending newline
				if ((flags & END_FILE_WHITESPACE_NEWLINE) > 0)
				{
					document += "\r\n";
				}
			}
			//Eliminate end of line whitespace
			if ((flags & END_LINE_WHITESPACE) > 0)
			{
				runRegex(@"(\x20|\t)+\r\n", "\r\n");
			}
			//Eliminate blank lines after braces
			if ((flags & BLANK_LINE_AFTER_BRACE) > 0)
			{
				runRegex(@"(\{\r\n)(\s)*\r\n((\x20|\t)*\S)", "$1$3");
			}
			//Eliminate blank lines before braces
			if ((flags & BLANK_LINE_BEFORE_BRACE) > 0)
			{
				runRegex(@"(\S)(\s)*(\r\n\})", "$1$3");
			}
		}

		/// <summary>
		/// Delegates work of creating and running regex to a
		/// helper function. Also runs it until it stops.
		/// </summary>
		/// <param name="regex">Regular expression string</param>
		/// <param name="replacement">Replacement string</param>
		private void runRegex(String regex, String replacement)
		{
			Regex reg_machine = new Regex(regex);
			document = reg_machine.Replace(document, replacement);
		}

		/// <summary>
		/// Gets the remove style property from the options page.
		/// </summary>
		/// <returns>Remove style flags</returns>
		private uint getStyleFlags()
		{
			uint ret = 0x0;
			//Get the environment
			DTE env = (DTE)Package.GetGlobalService(typeof(SDTE));
			if (env == null)
			{
				return 0x0;
			}
			//Get the prosperities in the environment
			EnvDTE.Properties props = env.get_Properties("CodeClean", "Style");
			//Go through each value and get the ones we want.
			foreach (Property p in props)
			{
				if (p.Name == "RemoveWhitespaceAtBottom" && (bool)p.Value)
				{
					ret |= END_FILE_WHITESPACE;
				}
				else if (p.Name == "RemoveWhitespaceAtBottomExceptNewline" && (bool)p.Value)
				{
					ret |= END_FILE_WHITESPACE_NEWLINE;
				}
				else if (p.Name == "RemoveWhitespaceAtTop" && (bool)p.Value)
				{
					ret |= BEGIN_FILE_WHITESPACE;
				}
				else if (p.Name == "RemoveWhitespaceEndLine" && (bool)p.Value)
				{
					ret |= END_LINE_WHITESPACE;
				}
				else if (p.Name == "RemoveConsecutiveBlankLines" && (bool)p.Value)
				{
					ret |= MULTIPLE_LINE_WHITESPACE;
				}
				else if (p.Name == "RemoveBlankAfterBrace" && (bool)p.Value)
				{
					ret |= BLANK_LINE_AFTER_BRACE;
				}
				else if (p.Name == "RemoveBlankBeforeBrace" && (bool)p.Value)
				{
					ret |= BLANK_LINE_BEFORE_BRACE;
				}
			}
			return ret;
		}
	}
}